﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO.Compression;
using System.Diagnostics;

namespace Toenda.Foundation.IO.Compression {
	public class ZipFile {
		public enum Compression {
			GZip = 0
		}

		public static event EventHandler<CompressionStateEventArgs> CompressionState;
		public static event EventHandler<CompressionFinishedEventArgs> CompressionFinished;

		private static ZipFileInfo GetZipInfo(string filename) {
			FileStream reader = File.OpenRead(filename);
			ZipFileInfo info = new ZipFileInfo();

			// read length
			byte[] bufferWrite = new byte[4];

			reader.Position = (int)reader.Length - 4;

			reader.Read(bufferWrite, 0, 4);

			info.Size = BitConverter.ToInt32(bufferWrite, 0);

			// read name
			byte bit = 1;

			reader.Position = 10;

			List<byte> nameBuffer = new List<byte>();

			while((bit = (byte)reader.ReadByte()) > 0) {
				//bit = (byte)this._output.ReadByte();

				if(bit > 0) {
					nameBuffer.Add(bit);
				}
			}

			//byte[] nameBuffer = new byte[nameLen];

			//this._output.Read(nameBuffer, 10, nameLen);

			info.Name = nameBuffer.ToArray().ToAsciiString();

			// go back to position 0
			reader.Position = 0;
			reader.Close();
			reader.Dispose();

			return info;
		}

		private static void CopyStream(Stream input, Stream output) {
			const int bufferSize = 4096;
			int read;
			byte[] buffer = new byte[bufferSize];

			long fileSize = long.MaxValue;
			long compressedBytes = 0;

			try {
				fileSize = input.Length;
			}
			catch(Exception) {
			}

			Stopwatch watch = new Stopwatch();

			watch.Start();

			while((read = input.Read(buffer, 0, buffer.Length)) > 0) {
				if(CompressionState != null) {
					if((compressedBytes + bufferSize) > fileSize) {
						compressedBytes = fileSize;
					}
					else {
						compressedBytes += bufferSize;
					}
				}

				output.Write(buffer, 0, read);

				if(CompressionState != null) {
					CompressionState(
						Type.GetType("Markit.FinancialFoundation.IO.Compression.ZipFile"),
						new CompressionStateEventArgs(compressedBytes, fileSize, "", watch.Elapsed)
					);
				}
			}

			watch.Stop();

			if(CompressionFinished != null) {
				CompressionFinished(
					Type.GetType("Markit.FinancialFoundation.IO.Compression.ZipFile"),
					new CompressionFinishedEventArgs(true, "", watch.Elapsed)
				);
			}

			//byte[] buffer = new byte[input.Length];
			//input.Read(buffer, 0, buffer.Length);

			//output.Write(buffer, 0, buffer.Length);
		}

		#region Compress

		public static void Compress(string filename) {
			string targetFilename = Path.ChangeExtension(filename, "gz");
			Compress(Compression.GZip, filename, targetFilename);
		}

		public static void Compress(string sourceFilename, string targetFilename) {
			Compress(Compression.GZip, sourceFilename, targetFilename);
		}

		public static void Compress(Compression mode, string sourceFilename, string targetFilename) {
			using(Stream input = File.OpenRead(sourceFilename)) {
				using(Stream output = File.OpenWrite(targetFilename)) {
					using(Stream gz = new NamedGZipStream(output, Path.GetFileName(sourceFilename), true)) {
						CopyStream(input, gz);
					}
				}
			}
		}

		#endregion

		#region Decompress

		public static void Decompress(string filename) {
			Decompress(Compression.GZip, filename, Path.GetDirectoryName(filename));
		}

		public static void Decompress(string filename, string targetPath) {
			Decompress(Compression.GZip, filename, targetPath);
		}

		public static void Decompress(Compression mode, string filename, string targetPath) {
			using(Stream input = File.OpenRead(filename)) {
				ZipFileInfo info = GetZipInfo(filename);
				string newFilename = targetPath + "\\" + info.Name;

				using(Stream output = File.OpenWrite(newFilename)) {
					using(Stream gz = new GZipStream(input, CompressionMode.Decompress, true)) {
						CopyStream(gz, output);
					}
				}
			}


			//using(Stream input = File.OpenRead(filename)) {
			//    //NamedGZipStream nameStream = new NamedGZipStream(input, null, CompressionMode.Decompress, false);
			//    ZipFileInfo info = GetZipInfo(filename);
			//    string newFilename = Path.GetDirectoryName(filename) + "\\___" + info.Name;
			//    //nameStream.Close();
			//    //nameStream.Dispose();

			//    using(Stream output = File.OpenWrite(newFilename)) {
			//        using(Stream gz = new GZipStream(input, CompressionMode.Decompress, true)) {
			//            CopyStream(gz, output);
			//        }
			//    }
			//}
		}

		#endregion
	}
}
